package test.serverframe.armc.server.manager.service.exec.dag;

import java.util.ArrayList;
import java.util.List;

/**
 * DAG 图形对象
 */
public class Dag {

    /**
     * 根节点
     */
    private List<DagNode> rootNodeList = new ArrayList<>();

    /**
     * 当前活动节点
     */
    private List<DagNode> livingNodeList = new ArrayList<>();

    /**
     * 全部节点
     */
    private List<DagNode> allNodeList = new ArrayList<>();

    /**
     * 节点激活处理接口
     */
    private NodeActivatedHandler nodeActivatedHandler;

    /**
     * 创建节点接口
     */
    private DagNodeCreator dagNodeCreator;

    /**
     * 锁对象，节点状态变更等不允许并行
     */
    private Object locker = new Object();


    /**
     * 根据节点信息列表创建DAG
     *
     * @param nodeInfos 要求没有重复的Id,在创建期间，如果nodeInfos发生改变，将会造成不可预料的后果
     * @return 返回创建的节点个数，包括可能为null的节点
     */
    public int createDag(List<NodeInfo> nodeInfos) {

        //首先清空列表
        allNodeList.clear();
        livingNodeList.clear();
        rootNodeList.clear();

        if (dagNodeCreator != null) {
            //循环创建所有的Node
            for (NodeInfo nodeInfo : nodeInfos) {
                DagNode node = dagNodeCreator.create(nodeInfo.id);
                node.setDag(this);
                allNodeList.add(node);
            }

            //根据nodeInfo，设置DagNode的相互关系
            int nodeCount = allNodeList.size();
            for (int i = 0; i < nodeCount; i++) {
                //根据顺序获取 Node 和 NodeInfo
                DagNode node = allNodeList.get(i);
                NodeInfo nodeInfo = nodeInfos.get(i);
                //判读是否有父节点
                if (nodeInfo.parents.size() == 0 || "".equals(nodeInfo.parents.get(0))) {
                    //没有父节点的情况，表示节点为根节点
                    rootNodeList.add(node);
                } else {
                    //设置 Node 的所有父节点
                    for (String parentId : nodeInfo.parents) {
                        DagNode parentNode = getNodeFromList(allNodeList, parentId);
                        node.addParent(parentNode);
                    }
                }
                //设置 Node 的所有子节点
                for (String childId : nodeInfo.children) {
                    DagNode childNode = getNodeFromList(allNodeList, childId);
                    node.addChild(childNode);
                }
            }
        }
        return allNodeList.size();
    }

    /**
     * 从节点列表中获取指定id的节点
     *
     * @param nodeList
     * @param id
     * @return
     */
    private DagNode getNodeFromList(List<DagNode> nodeList, String id) {
        for (DagNode node : nodeList) {
            if (node != null && node.getId().equals(id)) {
                return node;
            }
        }
        return null;
    }


    /**
     * 注入创建节点的方法
     *
     * @param dagNodeCreator
     */
    public void setNodeCreator(DagNodeCreator dagNodeCreator) {
        this.dagNodeCreator = dagNodeCreator;
    }

    /**
     * 开始执行
     */
    public void start() {
        synchronized (locker) {
            for (DagNode node : rootNodeList) {
                node.setState(DagNodeState.Living);
                livingNodeList.add(node);
                if (nodeActivatedHandler != null) {
                    nodeActivatedHandler.handle(node);
                }

            }
        }
    }


    /**
     * 获取当前活动节点列表
     *
     * @return
     */
    public DagNode[] getLivingNodes() {
        return (DagNode[]) livingNodeList.toArray();
    }

    /**
     * 处理节点死亡 (需要锁)
     * 处理规则：
     * 1、节点死亡后，首先将该节点从活动节点列表中删除
     * 2、遍历该节点的子节点，如果子节点可以激活，则激活子节点
     * 激活子节点的方式 ： 调用激活处理函数，更改状态为活动，然后放入活动节点列表中
     *
     * @param node
     */
    protected void handle_NodeIsDead(DagNode node) {
        synchronized (locker) {
            if (livingNodeList.contains(node)) {
                livingNodeList.remove(node);
            }
            if (node.getChildren() != null) {
                for (DagNode child : node.getChildren()) {
                    boolean nodeCanActivate = child.canActivate();
                    if (nodeCanActivate) {
                        child.setState(DagNodeState.Living);
                        livingNodeList.add(child);
                        if (nodeActivatedHandler != null) {
                            nodeActivatedHandler.handle(child);
                        }
                    }
                }
            }
        }
    }

    /**
     * 设置节点激活的处理函数 （无需锁）
     *
     * @param handler_onNodeActivated
     */
    public void setHandlerOnNodeActivated(NodeActivatedHandler handler_onNodeActivated) {
        this.nodeActivatedHandler = handler_onNodeActivated;
    }

}
